/*-----------------------------------------------------------------------------
File: Connect.cpp

NOTE: sometimes the VS won't launch when trying to debug the addin (You get an
      error message asking you for the "executable for debug session"). What I  
	  did to get around this is (in the message box that is asking for exectuble) 
	  enter for the path to the devenv.exe file  In my case, the path was:
	  C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\devenv.exe

History:
02/20/11  jnr	added functionality to find .cpp/.h files in the active vcproj
                from within the Addin code.
02/21/11  jnr	first attempt at breaking the code to get the active project 
				path into it's own file.
02/23/11  jnr   added protection against launching Addin without an open project
                changed signature of getPath() call
02/28/11  jnr   spun processing modules off into their own .cpp/.h files
03/05/11  jnr   prevent crash when processing a single file (not in a solution)
-----------------------------------------------------------------------------*/
#include "stdafx.h"
#include <vector>
#include <fstream>
#include <string>


#include "AddIn.h"
#include "globals.h"
#include "Connect.h"
#include "ProjectPath.h"
#include "ProcessInput.h"
#include "ProcessInputFiles.h"

extern CAddInModule _AtlModule;

CComPtr<EnvDTE80::DTE2> g_pDTE=NULL;
_ATL_FUNC_INFO OnClickButtonInfo = {CC_STDCALL, VT_EMPTY, 2, {VT_DISPATCH, VT_BYREF | VT_BOOL}};


// CConnect
STDMETHODIMP CConnect::OnConnection(IDispatch *pApplication, AddInDesignerObjects::ext_ConnectMode ConnectMode, IDispatch *pAddInInst, SAFEARRAY ** /*custom*/ )
{
	
	HRESULT hr = S_OK;
	pApplication->QueryInterface(__uuidof(EnvDTE80::DTE2), (LPVOID*)&m_pDTE);
	pAddInInst->QueryInterface(__uuidof(EnvDTE::AddIn), (LPVOID*)&m_pAddInInstance);

	//if( ConnectMode == AddInDesignerObjects::ext_cm_AfterStartup ||
	//	ConnectMode == AddInDesignerObjects::ext_cm_Startup) 
	{
		hr = AddToolBar();
		if(FAILED(hr))
			return hr;
	}	

	hr = CommandButton1Events::DispEventAdvise((IDispatch*)m_pButtonCPPDoc);
	if(FAILED(hr))
		return hr;

	//if(ConnectMode == 5) //5 == ext_cm_UISetup
	{
		HRESULT hr = S_OK;
		CComPtr<IDispatch> pDisp;
		CComQIPtr<EnvDTE::Commands> pCommands;
		CComQIPtr<Commands2> pCommands2;
		CComQIPtr<_CommandBars> pCommandBars;
		CComPtr<CommandBarControl> pCommandBarControl;
		CComPtr<EnvDTE::Command> pCreatedCommand;
		CComPtr<CommandBar> pMenuBarCommandBar;
		CComPtr<CommandBarControls> pMenuBarControls;
		CComPtr<CommandBarControl> pToolsCommandBarControl;
		CComQIPtr<CommandBar> pToolsCommandBar;
		CComQIPtr<CommandBarPopup> pToolsPopup;

		IfFailGoCheck(m_pDTE->get_Commands(&pCommands), pCommands);
		pCommands2 = pCommands;
		if(SUCCEEDED(pCommands2->AddNamedCommand2(m_pAddInInstance, CComBSTR("cplusplusdoc"), CComBSTR("cplusplusdoc"), CComBSTR("Executes the command for cplusplusdoc"), VARIANT_TRUE, CComVariant(59), NULL, EnvDTE::vsCommandStatusSupported+EnvDTE::vsCommandStatusEnabled, vsCommandStylePictAndText, vsCommandControlTypeButton, &pCreatedCommand)) && (pCreatedCommand))
		{
			//Add a button to the tools menu bar.
			IfFailGoCheck(m_pDTE->get_CommandBars(&pDisp), pDisp);
			pCommandBars = pDisp;

			//Find the MenuBar command bar, which is the top-level command bar holding all the main menu items:
			IfFailGoCheck(pCommandBars->get_Item(CComVariant(L"MenuBar"), &pMenuBarCommandBar), pMenuBarCommandBar);
			IfFailGoCheck(pMenuBarCommandBar->get_Controls(&pMenuBarControls), pMenuBarControls);

			//Find the Tools command bar on the MenuBar command bar:
			IfFailGoCheck(pMenuBarControls->get_Item(CComVariant(L"Tools"), &pToolsCommandBarControl), pToolsCommandBarControl);
			pToolsPopup = pToolsCommandBarControl;
			IfFailGoCheck(pToolsPopup->get_CommandBar(&pToolsCommandBar), pToolsCommandBar);

			//Add the control:
			pDisp = NULL;
			IfFailGoCheck(pCreatedCommand->AddControl(pToolsCommandBar, 1, &pDisp), pDisp);
		}
		return S_OK;
	}
	
Error:
	return hr;
}

STDMETHODIMP CConnect::OnDisconnection(ext_DisconnectMode /*RemoveMode*/, SAFEARRAY ** /*custom*/ )
{
	//m_pDTE = NULL;
	//m_pAddInInstance = NULL;

	HRESULT hr = CommandButton1Events::DispEventUnadvise((IDispatch*)m_pButtonCPPDoc);
	if(FAILED(hr))
		return hr;

	m_pDTE = NULL;
	return S_OK;
}

STDMETHODIMP CConnect::OnAddInsUpdate (SAFEARRAY ** /*custom*/ )
{
	return S_OK;
}

STDMETHODIMP CConnect::OnStartupComplete (SAFEARRAY ** /*custom*/ )
{
	return S_OK;
}

STDMETHODIMP CConnect::OnBeginShutdown (SAFEARRAY ** /*custom*/ )
{
	return S_OK;
}

STDMETHODIMP CConnect::QueryStatus(BSTR bstrCmdName, EnvDTE::vsCommandStatusTextWanted NeededText, EnvDTE::vsCommandStatus *pStatusOption, VARIANT *pvarCommandText)
{
  if(NeededText == EnvDTE::vsCommandStatusTextWantedNone)
	{
	  if(!_wcsicmp(bstrCmdName, L"cplusplusdoc.Connect.cplusplusdoc"))
	  {
		  *pStatusOption = (EnvDTE::vsCommandStatus)(EnvDTE::vsCommandStatusEnabled+EnvDTE::vsCommandStatusSupported);
	  }
  }
	return S_OK;
}

void processHTML(std::vector<char *> _prepro) {
	std::vector<char *> v_temp = _prepro;
	char *temp;
	char select;
	char *outgoing;
	std::ofstream htmldoc;
	// change name to match solution name
	// change file type to html
	htmldoc.open("temp.txt");
	htmldoc << "<html>";
	htmldoc << "<head>";
	htmldoc << "<title> CPlusPlus Doc for ... </title>";
	htmldoc << "<body";


	for (int i = 0; i < (int)_prepro.size(); i++) {
		temp = v_temp[i];
		select = temp[0];
		outgoing = &temp[1];
		switch (select) {
			case '0':
				htmldoc << outgoing;
				break;
			case '1':
				// process certain way
				break;
			default:
				break;
				// catch errors
		}
	}
	htmldoc << "</body>";
	htmldoc << "</html>";
	htmldoc.close();
	// insert code for generating html file
}

STDMETHODIMP CConnect::Exec(BSTR bstrCmdName, EnvDTE::vsCommandExecOption ExecuteOption, VARIANT * /*pvarVariantIn*/, VARIANT * /*pvarVariantOut*/, VARIANT_BOOL *pvbHandled)
{
	//cplusplusdoc();
	
	*pvbHandled = VARIANT_FALSE;
	
	// declaration for opening file, reading comments, and pushing into vector
	string infile; // declaration files.
    
    allComments  = new vector<Comment *>();
	
	//get the active project from the solution explorer window
	BSTR name;
	CComPtr<EnvDTE::Document> doc;
	m_pDTE->get_ActiveDocument(&doc);
	CComPtr<EnvDTE::ProjectItem> pitem;
	if(!doc)
	{
		MessageBox(0,"No open project or active file in view.", "Error", MB_OK);
		return S_OK;
	}
	doc->get_ProjectItem(&pitem);	
	pitem->get_FileNames(0,&name);
	
	//now the 'name' variable contains the path to the active file that is
	//open (currently being viewed in the IDE) which is part of the project (which
	//is what we're really after), but we need convert the bstr to LPCSTR so 
	//we can manipulate it...kinda funky.
	_bstr_t bstrStart(_T(name)); 
    TCHAR szFinal[MAX_PATH];
    _stprintf(szFinal, _T("%s"), (LPCTSTR)bstrStart);
	
	char *PathToProject = NULL;
	getPath(PathToProject, (char *)szFinal);
	
	FindFilesInProject(PathToProject);

	//display all comments
	
	//BEGIN TEMPORARY DEBUG CODE----------------------------------------
	//The below code is temporary. It's purpose is to display the contents
	//of the comment vector to a message box for easy viewing/debugging.
	//This code will also provide Brian with a template of how to extract
	//the data that needs to be written to HTML.
	char DebugDisplayVectorContents[1024];
	char buf[512];
	int size = allComments->size();
	vector <Comment *>::iterator citer;
	vector <string *>::iterator comlistiter;
	bool begin = true;
	
	// walking from first type of comment to the end comment
	for(citer = allComments->begin(); citer < allComments->end(); citer++)
	{	
		Comment *t = *citer;
		bool tagType = true;
		//only print the Filename if we're processing the first vector entry
		if(begin)
		{
			begin = false;
			sprintf(DebugDisplayVectorContents,"\nFileName: %s\n",t->getfileName().c_str());
		}
		else
		{		
			sprintf(buf,"\nFileName: %s\n",t->getfileName().c_str());
			strcat(DebugDisplayVectorContents,buf);
		}
	
		strcat(DebugDisplayVectorContents,"Comments:\n");
		vector <string> *temp = t->getAllComments();
        // display all comments in one type of comment
		for(int j=0; j<(int)temp->size(); j++)
		{
			//only display the tag type if we're processing the first comment entry
			if(tagType)
			{	
				tagType = false;
				sprintf(buf,"tag type = %s",temp->at(j).c_str());
				strcat(DebugDisplayVectorContents,buf);
			}
			else
			{
				strcat(DebugDisplayVectorContents,temp->at(j).c_str());
			}
			strcat(DebugDisplayVectorContents,"\n");
		}	
	}
	MessageBox(0, DebugDisplayVectorContents, "Contents of Comments Vector",MB_OK);
	//END TEMPORARY DEBUG CODE----------------------------------------

	if(ExecuteOption == EnvDTE::vsCommandExecOptionDoDefault)
	{
		if(!_wcsicmp(bstrCmdName, L"cplusplusdoc.Connect.cplusplusdoc"))
		{
			*pvbHandled = VARIANT_TRUE;
			return S_OK;
		}
	}
	return S_OK;
	
	
}

HRESULT CConnect::AddToolBar( )
{
	HRESULT hr = S_OK;

	CComQIPtr<_CommandBars> pCommandBars;
	CComPtr<IDispatch> pDisp;

	IfFailReturnCheck(m_pDTE->get_CommandBars(&pDisp), pDisp);
	pCommandBars = pDisp;

	CComPtr <CommandBar> pNewCommandBar;

	CComVariant vName(L"CPlusPlusDoc");
	CComVariant vPos(1);  // MsoBarPosition::msoBarTop = 1

	CComVariant vTemp(VARIANT_TRUE); // menu is temporary
	CComVariant vEmpty(DISP_E_PARAMNOTFOUND, VT_ERROR);

	IfFailReturnCheck(pCommandBars->Add(vName, vPos, vEmpty, vTemp, &pNewCommandBar),pNewCommandBar);

	//now get the toolband's CommandBarControls
	CComPtr<CommandBarControls> pBarControls;
	IfFailReturnCheck( pNewCommandBar->get_Controls(&pBarControls), pBarControls);

	IfFailReturn( AddButton(pBarControls, m_pButtonCPPDoc, IDB_BUT_1, L"cplusplusdoc", L"Launch CPlusPlusDoc"));
	
	//show the toolband //Changed the positions to have the same toolbar buttons order as in 6.0 version /SG

	pNewCommandBar->put_Visible(VARIANT_TRUE); 

	return hr;
}

STDMETHODIMP CConnect::cplusplusdoc()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	
	//*pvbHandled = VARIANT_FALSE;
	
	// declaration for opening file, reading comments, and pushing into vector
	string infile; // declaration files.
    
    allComments  = new vector<Comment *>();
	
	//get the active project from the solution explorer window
	BSTR name;
	CComPtr<EnvDTE::Document> doc;
	m_pDTE->get_ActiveDocument(&doc);
	CComPtr<EnvDTE::ProjectItem> pitem;
	if(!doc)
	{
		MessageBox(0,"No open project or active file in view.", "Error", MB_OK);
		return S_OK;
	}
	if(FAILED(doc->get_ProjectItem(&pitem)))
	{
		MessageBox(0,"Error with get_ProjectItem().", "Error", MB_OK);
		return S_OK;
	}
	if(FAILED(pitem->get_FileNames(0,&name)))
	{
		MessageBox(0,"Error with get_FileNames().", "Error", MB_OK);
		return S_OK;
	}
	
	//now the 'name' variable contains the path to the active file that is
	//open (currently being viewed in the IDE) which is part of the project (which
	//is what we're really after), but we need convert the bstr to LPCSTR so 
	//we can manipulate it...kinda funky.
	_bstr_t bstrStart(_T(name)); 
    TCHAR szFinal[MAX_PATH];
    _stprintf(szFinal, _T("%s"), (LPCTSTR)bstrStart);
	
	char *PathToProject = NULL;
	getPath(PathToProject, (char *)szFinal);
	
	FindFilesInProject(PathToProject);

	//display all comments
	
	//BEGIN TEMPORARY DEBUG CODE----------------------------------------
	//The below code is temporary. It's purpose is to display the contents
	//of the comment vector to a message box for easy viewing/debugging.
	//This code will also provide Brian with a template of how to extract
	//the data that needs to be written to HTML.
	char DebugDisplayVectorContents[1024];
	DebugDisplayVectorContents[0] = '\0';
	char buf[512];
	int size = allComments->size();
	vector <Comment *>::iterator citer;
	vector <string *>::iterator comlistiter;
	bool begin = true;
	
	// walking from first type of comment to the end comment
	for(citer = allComments->begin(); citer < allComments->end(); citer++)
	{	
		Comment *t = *citer;
		bool tagType = true;
		//only print the Filename if we're processing the first vector entry
		if(begin)
		{
			begin = false;
			sprintf(DebugDisplayVectorContents,"\nFileName: %s\n",t->getfileName().c_str());
		}
		else
		{		
			sprintf(buf,"\nFileName: %s\n",t->getfileName().c_str());
			strcat(DebugDisplayVectorContents,buf);
		}
	
		strcat(DebugDisplayVectorContents,"Comments:\n");
		vector <string> *temp = t->getAllComments();
        // display all comments in one type of comment
		for(int j=0; j<(int)temp->size(); j++)
		{
			//only display the tag type if we're processing the first comment entry
			if(tagType)
			{	
				tagType = false;
				sprintf(buf,"tag type = %s",temp->at(j).c_str());
				strcat(DebugDisplayVectorContents,buf);
			}
			else
			{
				strcat(DebugDisplayVectorContents,temp->at(j).c_str());
			}
			strcat(DebugDisplayVectorContents,"\n");
		}	
	}
	MessageBox(0, DebugDisplayVectorContents, "Contents of Comments Vector",MB_OK);
	//END TEMPORARY DEBUG CODE----------------------------------------

	//if(ExecuteOption == EnvDTE::vsCommandExecOptionDoDefault)
	//{
	//	if(!_wcsicmp(bstrCmdName, L"cplusplusdoc.Connect.cplusplusdoc"))
	//	{
			//*pvbHandled = VARIANT_TRUE;
	//		return S_OK;
	//	}
	//}
	return S_OK;

	
}

HRESULT CConnect::AddButton(CComPtr<CommandBarControls>& pBarControls, CComPtr<_CommandBarButton>& pButton,int idb, _bstr_t bstrNewCaption, _bstr_t bstrTipText)
{  
  HRESULT hr = S_OK;
  CComPtr<CommandBarControl> pNewBar;

  CComVariant vEmpty(DISP_E_PARAMNOTFOUND, VT_ERROR);
  CComVariant vToolBarType(1);  
  CComVariant vShow(VARIANT_TRUE);

  hr = pBarControls->Add(vToolBarType, vEmpty, vEmpty, vEmpty, vShow, &pNewBar);
  if( pNewBar== NULL)
    return E_FAIL;

  CComQIPtr<_CommandBarButton> pCmdButton(pNewBar);
  ATLASSERT(pCmdButton);

  AFX_MANAGE_STATE(AfxGetStaticModuleState());

  LPTSTR pp = MAKEINTRESOURCE(idb);

  HBITMAP hBmp =(HBITMAP)::LoadBitmap( AfxGetInstanceHandle( ), pp);

  DWORD ret = GetLastError();
  PICTDESC pd;
  ZeroMemory(&pd,sizeof(pd));  
  pd.cbSizeofstruct = sizeof(PICTDESC);
  pd.picType = PICTYPE_BITMAP;
  pd.bmp.hbitmap = hBmp;
  pd.bmp.hpal = NULL;

  Picture* spPictDisp;
  hr = OleCreatePictureIndirect(&pd,IID_IPictureDisp, TRUE,(void**) &spPictDisp);
  if(FAILED(hr))
  {
    MessageBox(0,_T("Error creating picture object!"),"",MB_OK|MB_TOPMOST);
    return hr;
  }
  pCmdButton->put_Style(msoButtonIcon);

  hr = pCmdButton->put_Picture(spPictDisp);
  if (FAILED(hr))
    return hr;

  ::DeleteObject(hBmp);

  pCmdButton->put_Visible( VARIANT_TRUE );
  pCmdButton->put_Caption( bstrNewCaption );
  pCmdButton->put_Enabled( VARIANT_TRUE );
  pCmdButton->put_TooltipText(bstrTipText );
  pCmdButton->put_Tag(bstrNewCaption );
  pCmdButton->put_Visible(VARIANT_TRUE);

  pButton = pCmdButton;

  return S_OK;
}
void __stdcall CConnect::OnClickButton( IDispatch*  Ctrl, VARIANT_BOOL * CancelDefault)
{
	USES_CONVERSION; 
	cplusplusdoc();
}
